<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>RAG智能问答系统</title>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    }

    body {
      background-color: #f8fafc;
      color: #1e293b;
      height: 100vh;
      overflow: hidden;
      display: flex;
      flex-direction: column;
    }

    .container {
      display: flex;
      flex: 1;
      max-height: 100vh;
      overflow: hidden;
    }

    /* 左侧聊天区域 */
    .chat-section {
      flex: 1;
      display: flex;
      flex-direction: column;
      border-right: 1px solid #e2e8f0;
      background-color: #ffffff;
      overflow: hidden;
    }

    .chat-header {
      padding: 18px 24px;
      background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
      color: white;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .chat-header h2 {
      font-weight: 600;
      font-size: 1.25rem;
    }

    .chat-header-info {
      font-size: 0.875rem;
      opacity: 0.9;
    }

    .chat-container {
      flex: 1;
      overflow-y: auto;
      padding: 20px;
      background-color: #f1f5f9;
      display: flex;
      flex-direction: column;
      gap: 16px;
    }

    .message {
      max-width: 80%;
      padding: 16px 20px;
      border-radius: 18px;
      position: relative;
      animation: fadeIn 0.3s ease;
      line-height: 1.6;
    }

    @keyframes fadeIn {
      from { opacity: 0; transform: translateY(10px); }
      to { opacity: 1; transform: translateY(0); }
    }

    .user-message {
      background-color: #3b82f6;
      color: white;
      align-self: flex-end;
      border-bottom-right-radius: 5px;
    }

    .bot-message {
      background: #f8f9fa;
      border-left: 4px solid #007bff;
    }

    .system-message {
      background: #fff3cd;
      border-left: 4px solid #ffc107;
      color: #856404;
      font-style: italic;
    }

    .input-container {
      padding: 16px 24px;
      background-color: white;
      border-top: 1px solid #e2e8f0;
      display: flex;
      flex-direction: column;
      gap: 10px;
    }

    .input-group {
      display: flex;
      flex-direction: column;
      gap: 10px;
    }

    .input-label {
      font-size: 12px;
      font-weight: 600;
      color: #374151;
      margin-bottom: 6px;
      display: block;
    }

    .input-group input {
      border: 1px solid #d1d5db;
      border-radius: 8px;
      padding: 10px 12px;
      font-size: 14px;
      outline: none;
      background-color: white;
      transition: border-color 0.2s;
    }

    .input-group input:focus {
      border-color: #3b82f6;
      box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
    }

    .input-wrapper {
      display: flex;
      background-color: white;
      border-radius: 12px;
      padding: 8px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
      border: 1px solid #cbd5e1;
    }

    .input-wrapper input {
      flex: 1;
      border: none;
      padding: 12px 16px;
      font-size: 16px;
      outline: none;
      background: transparent;
    }

    .input-wrapper button {
      background-color: #3b82f6;
      color: white;
      border: none;
      width: 46px;
      height: 46px;
      border-radius: 50%;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: background-color 0.2s;
    }

    .input-wrapper button:hover {
      background-color: #2563eb;
    }

    .input-wrapper button:disabled {
      background-color: #94a3b8;
      cursor: not-allowed;
    }

    .typing-indicator {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      padding: 10px;
      font-size: 14px;
      color: #64748b;
    }

    .typing-dot {
      width: 8px;
      height: 8px;
      background-color: #94a3b8;
      border-radius: 50%;
      animation: bounce 1.4s infinite ease-in-out both;
    }

    .typing-dot:nth-child(1) { animation-delay: -0.32s; }
    .typing-dot:nth-child(2) { animation-delay: -0.16s; }

    @keyframes bounce {
      0%, 80%, 100% { transform: scale(0); }
      40% { transform: scale(1); }
    }

    .source-info {
      margin-top: 12px;
      padding-top: 12px;
      border-top: 1px dashed #e2e8f0;
      font-size: 13px;
      color: #64748b;
    }

    .source-info i {
      margin-right: 6px;
    }

    /* 右侧检索结果区域 */
    .results-section {
      width: 380px;
      display: flex;
      flex-direction: column;
      background-color: #ffffff;
      overflow: hidden;
      border-left: 1px solid #e2e8f0;
    }

    .results-header {
      padding: 18px 24px;
      background-color: #f8fafc;
      border-bottom: 1px solid #e2e8f0;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .results-header h3 {
      font-weight: 600;
      font-size: 1.1rem;
      color: #374151;
    }

    .results-container {
      flex: 1;
      overflow-y: auto;
      padding: 16px;
    }

    .result-item {
      background-color: #f9fafb;
      border: 1px solid #e5e7eb;
      border-radius: 8px;
      padding: 16px;
      margin-bottom: 12px;
      transition: all 0.2s;
    }

    .result-item:hover {
      border-color: #3b82f6;
      box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1);
    }

    .result-title {
      font-weight: 600;
      color: #1f2937;
      margin-bottom: 8px;
      font-size: 14px;
    }

    .result-content {
      color: #6b7280;
      font-size: 13px;
      line-height: 1.5;
      margin-bottom: 8px;
    }

    .result-meta {
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 12px;
      color: #9ca3af;
    }

    .result-category {
      background-color: #e0e7ff;
      color: #3730a3;
      padding: 2px 8px;
      border-radius: 12px;
      font-size: 11px;
    }

    .result-score {
      font-weight: 500;
    }

    .result-scores {
      margin-top: 8px;
      padding-top: 8px;
      border-top: 1px solid #f3f4f6;
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
    }

    .score-item {
      background-color: #f0f9ff;
      color: #0369a1;
      padding: 2px 6px;
      border-radius: 4px;
      font-size: 11px;
      border: 1px solid #bae6fd;
    }

    .result-rank {
      font-weight: 500;
      color: #059669;
    }

    /* 控制面板 */
    .control-panel {
      padding: 16px;
      border-top: 1px solid #e2e8f0;
      background-color: #f8fafc;
    }

    .control-group {
      margin-bottom: 12px;
    }

    .control-label {
      font-size: 12px;
      font-weight: 600;
      color: #374151;
      margin-bottom: 6px;
      display: block;
    }

    .control-select {
      width: 100%;
      padding: 8px 12px;
      border: 1px solid #d1d5db;
      border-radius: 6px;
      font-size: 14px;
      background-color: white;
    }

    .control-button {
      width: 100%;
      padding: 8px 12px;
      background-color: #ef4444;
      color: white;
      border: none;
      border-radius: 6px;
      font-size: 14px;
      cursor: pointer;
      transition: background-color 0.2s;
    }

    .control-button:hover {
      background-color: #dc2626;
    }

    .control-button.secondary {
      background-color: #6b7280;
    }

    .control-button.secondary:hover {
      background-color: #4b5563;
    }

    /* 状态指示器 */
    .status-indicator {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 12px;
      color: #6b7280;
    }

    .status-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background-color: #10b981;
    }

    .status-dot.error {
      background-color: #ef4444;
    }

    .status-dot.warning {
      background-color: #f59e0b;
    }

    /* 响应式设计 */
    @media (max-width: 768px) {
      .results-section {
        width: 100%;
        border-left: none;
        border-top: 1px solid #e2e8f0;
      }
      
      .container {
        flex-direction: column;
      }
    }

    .button-group {
      display: flex;
      gap: 10px;
      justify-content: flex-end;
    }
    
    .button-group button {
      padding: 10px 20px;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      font-size: 14px;
      transition: background-color 0.3s;
    }
    
    .button-group button:hover {
      opacity: 0.8;
    }
  </style>
</head>
<body>
  <div class="container">
    <!-- 左侧聊天区域 -->
    <div class="chat-section">
      <div class="chat-header">
        <div>
          <h2>RAG智能问答系统</h2>
          <div class="chat-header-info">
            <span id="status-text">系统就绪</span>
          </div>
        </div>
        <div class="status-indicator">
          <div class="status-dot" id="status-dot"></div>
          <span id="connection-status">已连接</span>
        </div>
      </div>

      <div class="chat-container" id="chat-container">
        <div class="message bot-message">
          <div>👋 欢迎使用RAG智能问答系统！</div>
          <div class="source-info">
            <i class="fa fa-info-circle"></i>
            支持混合检索、流式对话和历史记录功能
          </div>
        </div>
      </div>

      <div class="input-container">
        <div class="input-group">
          <label class="input-label">文章标题</label>
          <input type="text" id="article-title" placeholder="请输入文章标题..." maxlength="200">
        </div>
        <div class="input-group">
          <textarea id="message-input" placeholder="请输入您的问题..." rows="3"></textarea>
          <div class="button-group">
            <button id="send-button" onclick="sendMessage()">
              <i class="fa fa-paper-plane"></i> 发送
            </button>
            <button id="stop-button" onclick="stopMessage()" style="display: none; background-color: #dc3545;">
              <i class="fa fa-stop"></i> 中断
            </button>
          </div>
        </div>
      </div>
    </div>

    <!-- 右侧检索结果区域 -->
    <div class="results-section">
      <div class="results-header">
        <h3>检索结果</h3>
        <span id="results-count">0 个结果</span>
      </div>

      <div class="results-container" id="results-container">
        <div style="text-align: center; color: #9ca3af; padding: 20px;">
          暂无检索结果
        </div>
      </div>

      <div class="control-panel">
        <div class="control-group">
          <label class="control-label">检索模式</label>
          <select class="control-select" id="search-type">
            <option value="hybrid">混合检索 (BM25+向量)</option>
            <option value="tfidf_expansion">TF-IDF扩展向量检索</option>
            <option value="improved_keyword">改进关键词检索</option>
            <option value="vector">向量检索</option>
            <option value="bm25">BM25检索</option>
            <option value="tfidf">TF-IDF检索</option>
          </select>
        </div>

        <div class="control-group">
          <button class="control-button secondary" onclick="checkHealth()">
            <i class="fa fa-heartbeat"></i> 系统状态
          </button>
          <button class="control-button secondary" onclick="clearHistory()">
            <i class="fa fa-trash"></i> 清空历史
          </button>
        </div>
      </div>
    </div>
  </div>

  <script>
    // 全局变量
    let isStreaming = false;
    let currentResponse = '';
    let eventSource = null;
    let abortController = null;  // 用于中断请求

    // DOM元素
    const chatContainer = document.getElementById('chat-container');
    const messageInput = document.getElementById('message-input');
    const sendButton = document.getElementById('send-button');
    const stopButton = document.getElementById('stop-button');
    const resultsContainer = document.getElementById('results-container');
    const resultsCount = document.getElementById('results-count');
    const statusDot = document.getElementById('status-dot');
    const statusText = document.getElementById('status-text');
    const connectionStatus = document.getElementById('connection-status');

    // 初始化
    document.addEventListener('DOMContentLoaded', function() {
      checkHealth();
      messageInput.addEventListener('keypress', function(e) {
        if (e.key === 'Enter' && !e.shiftKey) {
          e.preventDefault();
          sendMessage();
        }
      });
    });

    // 发送消息
    async function sendMessage() {
      const message = messageInput.value.trim();
      const articleTitle = document.getElementById('article-title').value.trim();
      
      if (!articleTitle) {
        alert('请先输入文章标题');
        return;
      }
      
      if (!message || isStreaming) return;

      // 添加用户消息
      addMessage(message, 'user');
      messageInput.value = '';
      sendButton.disabled = true;
      stopButton.style.display = 'inline-block';  // 显示中断按钮

      // 显示输入指示器
      showTypingIndicator();

      // 创建中断控制器
      abortController = new AbortController();

      try {
        const searchType = document.getElementById('search-type').value;
        
        console.log('发送请求:', {
          query: message,
          article_title: articleTitle,
          search_type: searchType,
          stream: true
        });
        
        // 发送流式请求
        const response = await fetch('/api/chat', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            query: message,
            article_title: articleTitle,
            search_type: searchType,
            stream: true
          }),
          signal: abortController.signal  // 添加中断信号
        });

        console.log('响应状态:', response.status);

        if (!response.ok) {
          const errorText = await response.text();
          throw new Error(`HTTP ${response.status}: ${errorText}`);
        }

        // 处理流式响应
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';

        isStreaming = true;
        currentResponse = '';

        while (true) {
          const { done, value } = await reader.read();
          if (done) break;

          buffer += decoder.decode(value, { stream: true });
          const lines = buffer.split('\n');
          buffer = lines.pop();

          for (const line of lines) {
            if (line.startsWith('data: ')) {
              try {
                const data = JSON.parse(line.slice(6));
                console.log('收到数据:', data);
                
                if (data.search_results) {
                  displaySearchResults(data.search_results);
                } else if (data.content) {
                  currentResponse += data.content;
                  updateBotMessage(currentResponse);
                } else if (data.error) {
                  throw new Error(data.error);
                } else if (data.done) {
                  // 流式响应完成
                  hideTypingIndicator();
                  isStreaming = false;
                  sendButton.disabled = false;
                  stopButton.style.display = 'none';  // 隐藏中断按钮
                  abortController = null;
                  
                  // 确保最终消息被正确保存
                  if (currentResponse.trim()) {
                    // 最终更新一次消息，确保内容完整
                    updateBotMessage(currentResponse);
                  }
                  return;
                }
              } catch (e) {
                console.error('解析流式数据失败:', e);
              }
            }
          }
        }

      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('请求被中断');
          addMessage('对话已中断', 'system');
        } else {
          console.error('发送消息失败:', error);
          addMessage(`发送失败: ${error.message}`, 'bot');
        }
        hideTypingIndicator();
        sendButton.disabled = false;
        stopButton.style.display = 'none';  // 隐藏中断按钮
        isStreaming = false;
        abortController = null;
      }
    }

    // 添加消息到聊天界面
    function addMessage(content, role) {
      const messageDiv = document.createElement('div');
      messageDiv.className = `message ${role}-message`;
      
      // 根据角色设置不同的样式
      if (role === 'system') {
        messageDiv.innerHTML = `<i class="fa fa-info-circle"></i> ${content}`;
      } else {
        messageDiv.innerHTML = content;
      }
      
      chatContainer.appendChild(messageDiv);
      chatContainer.scrollTop = chatContainer.scrollHeight;
    }

    // 更新机器人消息（流式）
    function updateBotMessage(content) {
      let botMessage = document.querySelector('.bot-message:last-child');
      
      // 如果最后一个消息是输入指示器，则移除它并创建新的机器人消息
      if (botMessage && botMessage.querySelector('.typing-indicator')) {
        botMessage.remove();
        botMessage = null;
      }
      
      // 如果没有机器人消息或最后一个消息是用户消息，则创建新的机器人消息
      if (!botMessage || botMessage.classList.contains('user-message')) {
        botMessage = document.createElement('div');
        botMessage.className = 'message bot-message';
        chatContainer.appendChild(botMessage);
      }
      
      // 更新消息内容
      botMessage.innerHTML = content;
      chatContainer.scrollTop = chatContainer.scrollHeight;
    }

    // 显示输入指示器
    function showTypingIndicator() {
      const indicator = document.createElement('div');
      indicator.className = 'message bot-message typing-indicator';
      indicator.innerHTML = `
        <div class="typing-dot"></div>
        <div class="typing-dot"></div>
        <div class="typing-dot"></div>
        <span>正在思考...</span>
      `;
      chatContainer.appendChild(indicator);
      chatContainer.scrollTop = chatContainer.scrollHeight;
    }

    // 隐藏输入指示器
    function hideTypingIndicator() {
      const indicator = document.querySelector('.typing-indicator');
      if (indicator) {
        indicator.remove();
      }
    }

    // 显示检索结果
    function displaySearchResults(results) {
      resultsContainer.innerHTML = '';
      resultsCount.textContent = `${results.length} 个结果`;

      if (results.length === 0) {
        resultsContainer.innerHTML = `
          <div style="text-align: center; color: #9ca3af; padding: 20px;">
            暂无检索结果
          </div>
        `;
        return;
      }

      results.forEach((result, index) => {
        const resultDiv = document.createElement('div');
        resultDiv.className = 'result-item';
        
        const scores = result.scores || {};
        
        // 创建得分显示HTML
        let scoreHtml = '';
        if (Object.keys(scores).length > 0) {
          scoreHtml = '<div class="result-scores">';
          Object.entries(scores).forEach(([key, value]) => {
            scoreHtml += `<span class="score-item"><strong>${key}:</strong> ${value}</span>`;
          });
          scoreHtml += '</div>';
        }

        resultDiv.innerHTML = `
          <div class="result-title">${result.title}</div>
          <div class="result-content">${result.content}</div>
          <div class="result-meta">
            <span class="result-category">${result.category}</span>
            <span class="result-rank">排名: ${result.rank}</span>
          </div>
          ${scoreHtml}
        `;
        
        resultsContainer.appendChild(resultDiv);
      });
    }

    // 清空历史记录
    async function clearHistory() {
      const articleTitle = document.getElementById('article-title').value.trim();
      
      try {
        const response = await fetch(`/api/history?article_title=${encodeURIComponent(articleTitle)}`, {
          method: 'DELETE'
        });
        
        if (response.ok) {
          // 清空聊天界面
          chatContainer.innerHTML = '';
          alert('历史记录已清空');
        } else {
          const error = await response.json();
          alert(`清空失败: ${error.error}`);
        }
      } catch (error) {
        console.error('清空历史失败:', error);
        alert(`清空失败: ${error.message}`);
      }
    }

    // 检查系统健康状态
    async function checkHealth() {
      try {
        const response = await fetch('/api/health');
        const data = await response.json();

        if (data.status === 'healthy') {
          statusDot.className = 'status-dot';
          statusText.textContent = '系统就绪';
          connectionStatus.textContent = '已连接';
        } else {
          statusDot.className = 'status-dot error';
          statusText.textContent = '系统异常';
          connectionStatus.textContent = '连接失败';
        }
      } catch (error) {
        console.error('健康检查失败:', error);
        statusDot.className = 'status-dot error';
        statusText.textContent = '连接失败';
        connectionStatus.textContent = '无法连接';
      }
    }

    // 中断消息
    function stopMessage() {
      if (abortController) {
        abortController.abort();
        abortController = null;
      }
      
      isStreaming = false;
      sendButton.disabled = false;
      stopButton.style.display = 'none';  // 隐藏中断按钮
      hideTypingIndicator();
      
      // 添加中断提示
      addMessage('对话已中断', 'system');
    }
  </script>
</body>
</html> 